﻿using EnvDTE;
using EnvDTE80;
using Microsoft;
using System.Reflection;
using VSLangProj;

namespace Single.Generate
{
    public class Generate : IDisposable
    {
        private const string SolutionName = "Single";
        private const string ServiceFolder = "4.业务模块";
        private const string DirectoryName = "net8.0";
        private const string VisualStudioDTE = "VisualStudio.DTE.17.0"; // 注册表 VisualStudio.DTE -- CurVer 值

        private DTE dte;
        private string path;
        private Solution solution;
        private string solutionConfiguration;
        private List<Project> projects;

        public Generate(string currentName)
        {
            path = Environment.CurrentDirectory;
            path = path.Substring(0, path.IndexOf($"{SolutionName}.{currentName}") - 1);
        }

        public void GenerateSolutionInit()
        {
            Console.WriteLine("初始化 VisualStudio.DTE 中...");
            dte = (DTE)Activator.CreateInstance(Type.GetTypeFromProgID(VisualStudioDTE));

            Console.WriteLine("正在加载解决方案...");
            dte.Solution.Open($@"{path}\{SolutionName}.sln");

            Assumes.Present(dte);
            solution = dte.Solution;
            solutionConfiguration = solution.SolutionBuild.ActiveConfiguration.Name;

            Console.WriteLine("正在读取业务模块...");
            projects = GetProjects(solution.Projects, ServiceFolder);
        }

        public GenerateResult GenerateAll()
        {
            GenerateSolutionInit();

            projects.ForEach(project =>
            {
                var list = GetEntitiesByProject(project);

                ServiceGenerate(path, project.Name, true, list);
            });

            Console.WriteLine("执行完毕");

            return GenerateResult.SuccessResult("全量生成执行完毕");
        }

        public GenerateResult GenerateSingle(string name)
        {
            GenerateSolutionInit();

            var project = GetProject($"{name}");

            if (project != null)
            {
                var list = GetEntitiesByProject(project);

                ServiceGenerate(path, project.Name, true, list);

                Console.WriteLine("执行完毕");

                return GenerateResult.SuccessResult($"项目 {name} 生成完毕");
            }

            return GenerateResult.FailResult("项目尚未创建");
        }

        public GenerateResult GenerateEntity(string area, string tenant, string entity, Dictionary<string, string> columns)
        {
            Console.WriteLine($"正在创建 {SolutionName}.{area}.{entity} 实体...");

            string projectFolder = $@"{path}\{SolutionName}.{area}";

            if (Directory.Exists(projectFolder))
            {
                T4.Entity t4 = new T4.Entity(SolutionName, area, tenant, entity, columns);
                ClassFileWrite($@"{projectFolder}\Entity", $@"{entity}.cs", t4.TransformText(), false);

                ServiceGenerate(path, $"{SolutionName}.{area}", false, new List<string> { entity });

                return GenerateResult.SuccessResult("创建成功");
            }

            return GenerateResult.FailResult("项目尚未创建");
        }

        public object GetProjectsInfo()
        {
            GenerateSolutionInit();

            var list = projects.Select(m => new
            {
                name = m.Name,
                fullName = m.FullName
            });

            return list.ToList();
        }

        private List<Project> GetProjects(Projects projects, string folder)
        {
            List<Project> list = new List<Project>();

            foreach (Project project in projects)
            {
                if (project.Kind.Equals(ProjectKinds.vsProjectKindSolutionFolder))
                {
                    if (project.Name.Equals(folder))
                    {
                        foreach (ProjectItem item in project.ProjectItems)
                        {
                            list.Add(item.SubProject);
                            Console.WriteLine(item.SubProject.Name);
                        }
                    }
                }
            }

            return list;
        }

        private Project GetProject(string projectName)
        {
            return projects.FirstOrDefault(m => m.Name == projectName);
        }

        private List<string> GetEntitiesByProject(Project project)
        {
            Console.WriteLine($"正在生成 {project.Name} 项目...");

            solution.SolutionBuild.BuildProject(solutionConfiguration, project.FullName, true);

            string path = project.FullName.Replace($@"\{project.Name}.csproj", string.Empty);

            var assemblyFile = $@"{path}\bin\{solutionConfiguration}\{DirectoryName}\{project.Name}.dll";

            Console.WriteLine($"正在反射 {assemblyFile}");

            Assembly assembly = Assembly.Load(File.ReadAllBytes(assemblyFile));

            Type[] types;

            try
            {
                types = assembly.GetTypes();
            }
            catch
            {
                assembly = Assembly.LoadFrom(assemblyFile);

                types = assembly.GetTypes();
            }

            types = types.Where(m => m.Namespace == $"{project.Name}.Entity").ToArray();

            return types.Select(m => m.Name).ToList();
        }

        private void ServiceGenerate(string path, string projectName, bool generateProp, List<string> list)
        {
            var area = projectName.Replace($"{SolutionName}.", string.Empty);

            foreach (var item in list)
            {
                T4.Service t4 = new T4.Service(SolutionName, area, item);
                ClassFileWrite($@"{path}\{projectName}\Service", $@"{item}Service.cs", t4.TransformText(), false);
            }

            if (generateProp) { ServicePropsGenerate(path, projectName, area, list); }
        }

        private void ServicePropsGenerate(string path, string projectName, string area, List<string> list)
        {
            T4.ServiceProps t4 = new T4.ServiceProps(SolutionName, area, list);
            ClassFileWrite($@"{path}\{projectName}", @"ServiceProps.cs", t4.TransformText(), true);
        }

        private void ClassFileWrite(string path, string file, string text, bool cover)
        {
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }

            string filename = $@"{path}\{file}";

            if (!File.Exists(filename) || cover)
            {
                File.WriteAllText(filename, text);
            }

            Console.WriteLine($@"生成文件:{path}\{file}");
        }

        #region 释放资源

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            Dispose(true); GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        /// <param name="disposing">是否释放</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (solution != null)
                {
                    solution.Close();
                }
                if (dte != null)
                {
                    dte.Quit();
                }
            }
        }

        #endregion
    }
}